module DevWisdom
  module MigrationUtils
    def self.included(base)
      base.extend(ClassMethods)
    end

    module ClassMethods
      def add_foreign_key(from_table, to_table)
        col_name = to_table.to_s.singularize + "_id"
        add_column(from_table, col_name, :integer)
        foreign_key(from_table, col_name, to_table)
      end

      def remove_foreign_key(from_table, to_table)
        col_name = to_table.to_s.singularize + "_id"
        execute("alter table #{from_table} drop foreign key fk_#{from_table}_#{col_name}")
        remove_column(from_table, col_name)
      end

      def remove_foreign_key_column(from_table, col_name)
        execute("alter table #{from_table} drop foreign key fk_#{from_table}_#{col_name}")
        remove_column(from_table, col_name)
      end
    
      def foreign_key(from_table, from_column, to_table, to_column = "id")
        constraint_name = "fk_#{from_table}_#{from_column}"

        sql = <<-SQL
          alter table #{from_table} add constraint #{constraint_name}
          foreign key (#{from_column})
            references #{to_table}(#{to_column})
        SQL

        execute(sql)
      end

      def create_join_table(table_one, table_two, fk = true)
        c1 = "#{table_one.to_s.singularize}_id"
        c2 = "#{table_two.to_s.singularize}_id"
        lt =  "#{table_one}_#{table_two}"

        create_table(lt, :id => false) do |t|
          t.column c1, :integer, :null => false
          t.column c2, :integer, :null => false
        end

        pkey = "alter table #{lt} add primary key (#{c1}, #{c2})"
        execute(pkey)

        if fk
          foreign_key(lt, c1, table_one)
          foreign_key(lt, c2, table_two)
        end
      end

      def drop_join_table(table_one, table_two)
        drop_table("#{table_one}_#{table_two}")
      end

      def create_reference_table(tname, *referenced_by)
        create_table(tname) do |t|
          t.column(:name, :string)
        end

        referenced_by.each do |rtable|
          add_foreign_key(rtable, tname)
        end
      end

      def drop_reference_table(tname, *referenced_by)
        referenced_by.each do |rtable|
          remove_foreign_key(rtable, tname)
        end

        drop_table(tname)
      end
      
      #
      # Methods for LockDown
      #
      
      def rest_urls_for(sobj)
        rest_urls.collect{|url| sobj + "/" + url}
      end
      
      def rest_urls
        %w(index show new edit create update destroy)
      end
      
      def create_static_system_object(sname, smethods, roles)
        sobj = SystemObject.create(:name => sname, :can_remove => false)
        initialize_system_object(sobj, smethods, roles)
      end
      
      def create_system_object(sname, smethods, roles)
        sobj = SystemObject.create(:name => sname)
        initialize_system_object(sobj, smethods, roles)
      end
        
      def initialize_system_object(sobj, smethods, roles)
        if roles.kind_of? Array
          roles.each{|role| role.add_system_object(sobj)}
        else
          roles.add_system_object(sobj)
        end

        smethods.each{|smethod| SystemMethod.create(:name => smethod, :system_object_id => sobj.id)}
      end

      def add_system_methods(object_name, smethods)
        sobj = SystemObject.find(:first, :conditions => "name = '#{object_name}'")

        smethods.each{|smethod| SystemMethod.create(:name => smethod, :system_object_id => sobj.id)}
      end

      def remove_system_methods(smethods)
        smethods.each{|smethod| SystemMethod.find(:first, :conditions => "name = '#{smethod}'").destroy}
      end

      def remove_system_object(sobject)
        sobj = SystemObject.find(:first, :conditions => "name = '#{sobject}'")
        sobj.role.each{|r| r.delete_system_object(sobj)}
        sobj.destroy
      end

      def add_system_object_to_role(sobject, role)
        sobj = SystemObject.find(:first, :conditions => "name = '#{sobject}'")
        role.add_system_object(sobj)
      end

      def remove_system_object_from_role(sobject, role)
        sobj = SystemObject.find(:first, :conditions => "name = '#{sobject}'")
        role.delete_system_object(sobj)
      end
    end #class methods
  end#MigrationUtils
end # module DevWisdom

ActiveRecord::Migration.send :include, DevWisdom::MigrationUtils
